//
//  BBNode.m
//  xNose
//
//  Created by ben smith on 9/7/08.
//  Copyright 2008 __MyCompanyName__. All rights reserved.
//

#import "BBNode.h"


@implementation BBNode

@synthesize cost;
@synthesize income;
@synthesize title;
@synthesize subtitle;
@synthesize connections;
@synthesize children;
@synthesize isConnecting;
@synthesize information;
@synthesize type;
@synthesize isOpen;
@synthesize isHidden;
@synthesize position;
@synthesize view;
@synthesize parent;
@synthesize layer;


- (id)initWithCoder:(NSCoder *)coder
{
	if (![self init]) return nil;
	
	isHidden = NO;
	self.title = [coder decodeObjectForKey:@"title"];
	self.subtitle = [coder decodeObjectForKey:@"subtitle"];
	self.parent = [coder decodeObjectForKey:@"parent"];
	self.connections = [coder decodeObjectForKey:@"connections"];
	self.information = [coder decodeObjectForKey:@"information"];
	
	self.isOpen = [coder decodeBoolForKey:@"isOpen"];
	self.position = [coder decodePointForKey:@"position"];
	
	self.cost = [coder decodeFloatForKey:@"cost"];
	self.income = [coder decodeFloatForKey:@"income"];
	
	self.type = [coder decodeObjectForKey:@"type"];
	NSArray * kids = [coder decodeObjectForKey:@"children"];
	self.children = [NSMutableArray arrayWithArray:kids];
	
	NSIndexSet * all = [NSIndexSet indexSetWithIndexesInRange:NSMakeRange(0, [self.children count])];
	
	[self.children addObserver:self toObjectsAtIndexes:all forKeyPath:@"cost" options:NSKeyValueObservingOptionNew context:nil];
	[self.children addObserver:self toObjectsAtIndexes:all forKeyPath:@"income" options:NSKeyValueObservingOptionNew context:nil];
	// be sure to calc the bounding rect
	return self;
}

// encode myself
- (void)encodeWithCoder:(NSCoder *)coder
{	
	[coder encodeFloat:cost forKey:@"cost"];
	[coder encodeFloat:income forKey:@"income"];
	[coder encodeBool:isOpen forKey:@"isOpen"];
	[coder encodePoint:position forKey:@"position"];
	[coder encodeObject:information forKey:@"information"];
	[coder encodeObject:type forKey:@"type"];
	[coder encodeObject:title forKey:@"title"];
	[coder encodeObject:subtitle forKey:@"subtitle"];
	[coder encodeObject:connections forKey:@"connections"];
	[coder encodeObject:parent forKey:@"parent"];
	[coder encodeObject:children forKey:@"children"];
}

- (id) init
{
	self = [super init];
	if (self != nil) {
		isHidden = NO;
		self.cost = 1000;
		self.income = 1200;
		self.title = @"Lorem Ipsum";
		self.subtitle = @"Sit Amet";
		self.information = @"Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Maecenas tincidunt sagittis odio. Vestibulum porta mi eu ipsum. Morbi vel mauris sed dui dapibus euismod. Phasellus adipiscing sodales ante. Vestibulum tincidunt.";
		self.position = NSMakePoint(1024, 400);
	}
	return self;
}



-(BOOL)isRoot
{
	return [self.type isEqualToString:@"root"];	
}

-(BOOL)isCollection
{
	return [self.type isEqualToString:@"collection"];
}

-(BOOL)isContainer
{
	return [self.type isEqualToString:@"container"];
}

-(BOOL)isLeaf
{
	return [self.type isEqualToString:@"leaf"];
}

-(BBNode*)deepCopy
{
	NSData * archive = [NSKeyedArchiver archivedDataWithRootObject:self];
	BBNode *copy = [NSKeyedUnarchiver unarchiveObjectWithData:archive];
	return copy;	
}

-(BOOL)hasChildren
{
	if (children == nil) return NO;
	if ([children count] == 0) return NO;
	return YES;
}

-(float)profit
{
	float roundTo = [[NSUserDefaults standardUserDefaults] floatForKey:@"BBRadialRoundValue"];

	float costRounded = roundf(self.cost / roundTo);
	costRounded *= roundTo;
	
	float incRounded = roundf(self.income / roundTo);
	incRounded *= roundTo;	

	return (incRounded - costRounded)/costRounded * 100.0;
}

-(float)cost
{
	if (![self hasChildren]) {
		return cost;		
	}
	float total = 0.0;
	for (BBNode* kid in children) {
		total += [kid cost];
	}
	return total;
}

-(float)income
{
	if (![self hasChildren]) {
		return income;		
	}
	float total = 0.0;
	for (BBNode* kid in children) {
		total += [kid income];
	}
	return total;
}

- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
{
	[self willChangeValueForKey:@"cost"];
	[self willChangeValueForKey:@"income"];
	[self didChangeValueForKey:@"cost"];
	[self didChangeValueForKey:@"income"];
}


-(void)addConnection:(BBNode*)aNode
{
	if (connections == nil)
		self.connections = [NSMutableArray array];
	[connections addObject:aNode];
} 


-(BOOL)isConnectedTo:(BBNode*)aNode
{
	return [connections containsObject:aNode];
}

-(void)removeConnectedNode:(BBNode*)aNode recurse:(BOOL)recurse
{
	[connections removeObject:aNode];
	if (!recurse) return;
	for (BBNode* kid in self.children) {
		[kid removeConnectedNode:aNode recurse:recurse];
	}
}

-(void)clearConnections
{
	if (connections == nil)
		self.connections = [NSMutableArray array];
	[connections removeAllObjects];	
}

-(void)insertChild:(BBNode*)aNode atIndex:(NSInteger)index
{
	[self willChangeValueForKey:@"hasChildren"];
	if (children == nil)
		self.children = [NSMutableArray array];
	[children insertObject:aNode atIndex:index];	
	[aNode setParent:self];
	[aNode addObserver:self forKeyPath:@"cost" options:NSKeyValueObservingOptionNew context:nil];
	[aNode addObserver:self forKeyPath:@"income" options:NSKeyValueObservingOptionNew context:nil];
	[self didChangeValueForKey:@"hasChildren"];
}


-(void)addChild:(BBNode*)aNode
{
	[self willChangeValueForKey:@"hasChildren"];
	if (children == nil)
		self.children = [NSMutableArray array];
	[children addObject:aNode];
	[aNode setParent:self];
	[aNode addObserver:self forKeyPath:@"cost" options:NSKeyValueObservingOptionNew context:nil];
	[aNode addObserver:self forKeyPath:@"income" options:NSKeyValueObservingOptionNew context:nil];
	[self didChangeValueForKey:@"hasChildren"];
}

-(void)removeChild:(BBNode*)aNode
{
	[self willChangeValueForKey:@"hasChildren"];
	if (children == nil) return;
	if ([children containsObject:aNode]) {
		[aNode setParent:nil];
		[children removeObject:aNode];
		[aNode removeObserver:self forKeyPath:@"cost"];
		[aNode removeObserver:self forKeyPath:@"income"];		
	}
	[self didChangeValueForKey:@"hasChildren"];
}

-(BOOL)isSameTypeAs:(BBNode*)aNode
{
	return [self.type isEqualToString:aNode.type];
}




///////////////////////////////////////////////////////
// view manipulation methods
// these should be moved to another object

-(void)setViewScale:(float)scale
{
	// set the identity unit square first, otherwise the scales add up
	[self.view scaleUnitSquareToSize:NSMakeSize(scale, scale)];
	[self.view setFrame:[self.view centerScanRect:[self.view frame]]];
	[self.view setNeedsDisplay:YES];
	for (BBNode* kid in self.children) {
		[kid setViewScale:scale];
	}			
}


-(void)resetViews:(BOOL)animate
{
	NSRect f = [self.view frame];
	f.origin = self.position;
	if (animate) {
		[[self.view animator] setFrame:[self.view centerScanRect:f]];
	} else {
		[self.view setFrame:[self.view centerScanRect:f]];		
	}
	for (BBNode* kid in self.children) {
		[kid resetViews:animate];
	}
}

-(void)removeFromView
{
	[self.view removeFromSuperview];
	for (BBNode* kid in self.children) {
		[kid removeFromView];
	}
}

-(void)hideView
{
	if (isHidden) return;
	isHidden = YES;
	savedFrame = [self.view frame];
	[self.view setFrame:NSMakeRect(-2000, -2000, 100, 100)];
	for (BBNode* kid in self.children) {
		[kid hideView];
	}	
}

-(void)setIsHidden:(BOOL)b
{
	isHidden = b;
	for (BBNode* kid in self.children) {
		[kid setIsHidden:b];
	}			
}

-(void)showView
{
	if (!isHidden) return;
	isHidden = NO;
	[self.view setFrame:savedFrame];
	for (BBNode* kid in self.children) {
		[kid showView];
	}		
}

///////////////////////////////////////////////////////
///////////////////////////////////////////////////////


///////////////////////////////////////////////////////
// whole tree manipulation methods
// these should be moved to another object

-(void)openConnected
{
	// go through my kids, if any are connected, then i should open
	if ([self isLeaf]) {
		for (BBNode* connection in self.connections) {
			connection.parent.isOpen = YES;
		}
		return;
	}
	if ([self isCollection]) {
		for (BBNode* kid in self.children) {
			if ([kid.connections count] > 0) {
				self.isOpen = YES;
			}
		}
	}
	for (BBNode* kid in self.children) {
		[kid openConnected];
	}
}

-(BBNode*)firstLeaf
{
	if ([self isLeaf]) return self;
	if ([self hasChildren]) {
		for (BBNode* kid in self.children) {
			BBNode* leaf = [kid firstLeaf];
			if (leaf != nil) return leaf;
		}
	}
	return nil;
}

-(BBNode*)firstCollection
{
	if ([self isCollection]) return self;
	if ([self isLeaf]) return self.parent;
	if ([self hasChildren]) {
		for (BBNode* kid in self.children) {
			BBNode* collection = [kid firstCollection];
			if (collection != nil) return collection;
		}
	}
	return nil;
}

-(BBNode*)firstContainer
{
	if ([self isCollection]) return self.parent;
	if ([self isLeaf]) return self.parent.parent;
	if ([self hasChildren]) {
		for (BBNode* kid in self.children) {
			BBNode* container = [kid firstContainer];
			if (container != nil) return container;
		}
	}
	return nil;
}


-(BBNode*)hitTest:(NSPoint)p
{
	// first check my kids, if i am open
	if ([self isOpen] || ![self isCollection]) {
		for (BBNode* kid in self.children) {
			BBNode * hit = [kid hitTest:p];
			if (hit != nil) return hit;
		}		
	}
	if (self.parent == nil) return nil; // we are the root, cant hit us!
	if (NSPointInRect(p, self.view.frame)) return self;
	return nil;
}


-(void)shiftViewFrameBy:(NSPoint)vector animate:(BOOL)animate
{
	NSRect f = [self.view frame];
	f.origin.x += vector.x;
	f.origin.y += vector.y;
	if (animate) {
		[[self.view animator] setFrame:f];
	} else {
		[self.view setFrame:f];		
	}
	for (BBNode* kid in self.children) {
		[kid shiftViewFrameBy:vector animate:animate];
	}			
}


///////////////////////////////////////////////////////
///////////////////////////////////////////////////////


///////////////////////////////////////////////////////
// class methods
///////////////////////////////////////////////////////

+(BBNode*) node;
{
	BBNode * newNode = [[BBNode alloc] init];
	return [newNode autorelease];
}

+(BBNode*) rootNode;
{
	BBNode * newNode = [[BBNode alloc] init];
	newNode.type = @"root";
	return [newNode autorelease];
}

+(BBNode*) containerNode;
{
	BBNode * newNode = [[BBNode alloc] init];
	newNode.type = @"container";
	return [newNode autorelease];
}

+(BBNode*) collectionNode;
{
	BBNode * newNode = [[BBNode alloc] init];
	newNode.type = @"collection";
	return [newNode autorelease];
}

+(BBNode*) leafNode;
{
	BBNode * newNode = [[BBNode alloc] init];
	newNode.type = @"leaf";
	return [newNode autorelease];
}



/////////////////////////////////////////////
// deallocation

- (void) dealloc
{
	NSIndexSet * all = [NSIndexSet indexSetWithIndexesInRange:NSMakeRange(0, [children count])];
	[children removeObserver:self fromObjectsAtIndexes:all forKeyPath:@"cost"];
	[children removeObserver:self fromObjectsAtIndexes:all forKeyPath:@"income"];
	[children release];
	[connections release];
	[view release];
	self.title = nil;
	[super dealloc];
}


@end
